package com.example.controller;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.example.base.BaseController;
import com.example.constant.RedisConstance;
import com.example.entity.User;
import com.example.entity.UserRole;
import com.example.mapper.UserMapper;
import com.example.security.custom.SmsCodeAuthenticationToken;
import com.example.security.handler.SecurityAuthenticationFailureHandler;
import com.example.security.handler.SecurityAuthenticationSuccessHandler;
import com.example.security.handler.SmsCodeAuthenticationFailureHandler;
import com.example.service.CartInfoService;
import com.example.service.FootPrintService;
import com.example.service.UserRoleService;
import com.example.service.UserService;
import com.example.util.RedisUtil;
import com.example.util.SmsUtils;
import com.example.util.TLSSigAPIv2;
import com.example.util.ValidateCodeUtils;
import com.example.vo.*;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.time.Instant;
import java.util.Map;
import java.util.regex.Pattern;


@RestController
@Slf4j
public class UserController extends BaseController<UserService, User> {
    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private CartInfoService cartInfoService;

    @Autowired
    private FootPrintService footPrintService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SecurityAuthenticationSuccessHandler securityAuthenticationSuccessHandler;

    @Autowired
    private SecurityAuthenticationFailureHandler securityAuthenticationFailureHandler;

    @Autowired
    private SmsCodeAuthenticationFailureHandler smsCodeAuthenticationFailureHandler;

    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    @Resource
    private AuthenticationManager authenticationManager;


    @Resource
    private TLSSigAPIv2 tlsSigAPIv2;



    @PostMapping("/user/sendMsg")
    @Operation(summary = "发送验证码")
    public Result<String> sendMsg(@RequestBody User user, HttpSession session, @RequestParam(required = false) String action){
        String phone = user.getPhone();

        if (StrUtil.isBlank(phone)) {
            return Result.error("手机号不能为空");
        }

        if (!validatePhoneNumber(phone)) {
            return Result.error("手机号填写错误");
        }

        boolean userExists = baseService.existsByPhone(phone);
        if ("register".equals(action) && userExists) {
            return Result.error("该手机号已注册，请直接登录");
        } else if (("resetPassword".equals(action) || "login".equals(action)) && !userExists) {
            return Result.error("该手机号未注册，请先注册");
        }

        // 生成四位验证码和时间戳
        Object[] result = ValidateCodeUtils.generateValidateCodeWithTimestamp(4);
        String code = result[0].toString();
        long timestamp = (long) result[1];
        log.info("code: {}", code);

        // 将验证码和时间戳保存到 session 中
        session.setAttribute(phone + "_code", code);
        session.setAttribute(phone + "_timestamp", timestamp);

        // 调用阿里云提供的短信服务
        SmsUtils.sendMessage(phone,code);

        return Result.ok("验证码发送成功,将于5分钟后过期");
    }

    @PostMapping("/user/register")
    @Operation(summary = "注册")
    public Result<?> register(@RequestBody RegisterUser user, HttpSession session) {
        //主键
        Long timeMillis = (System.currentTimeMillis() / 1000);
        user.setId(timeMillis);

        String phone = user.getPhone();
        String password = user.getPassword();
        String confirmPassword = user.getConfirmPassword();
        String verifyCode = user.getVerifyCode();

        boolean isValidPassword = password.matches("^(?=.*[A-Za-z])(?=.*\\d).{8,}$");

        if (!isValidPassword) {
            return Result.error("密码需由字母和数字组成，且长度至少为8");
        } else if (!password.equals(confirmPassword)) {
            return Result.error("两次输入的密码不一致");
        } else if (!validatePhoneNumber(phone)) {
            return Result.error("手机号格式不正确");
        } else if (baseService.existsByPhone(phone)) {
            return Result.error("手机号已被注册");
        }

        // 验证验证码
        if (!verifyCode(phone, verifyCode, session)) {
            return Result.error("验证码错误或已过期");
        }

        String encode = passwordEncoder.encode(password);
        user.setPassword(encode);
        user.setHaveStore(false);
        boolean isSaved = baseService.register(user);

        if (isSaved) {
            Long userId = baseService.getIdByPhone(phone);
            UserRole userRole = new UserRole();
            userRole.setUser_id(userId);
            userRole.setRole_id(2L);
            userRoleService.save(userRole);
            return Result.ok("注册成功");
        } else {
            return Result.error("注册失败，请稍后重试");
        }
    }

    @SneakyThrows
    @PostMapping("/user/login")
    @Operation(summary = "密码登录")
    public Result<String> login(@RequestBody User user, HttpServletRequest request, HttpServletResponse response) {
        String phone = user.getPhone();
        String password = user.getPassword();

        try {
            // 构建 Authentication 对象
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(phone, password);
            // 进行认证
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            // 认证成功时调用
            redisUtil.set(RedisConstance.LOGIN_USER_TOKEN + phone, JSON.toJSONString(authentication.getPrincipal()));
            securityAuthenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);
        } catch (BadCredentialsException e) {
            // 认证失败时调用
            securityAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
        } catch (AuthenticationException e) {
            return Result.error(e.getMessage());
        }
        return null;

    }

    @SneakyThrows
    @PostMapping("/user/verifyLogin")
    @Operation(summary = "验证码登录")
    public Result<User> verifyLogin(@RequestBody Map map, HttpServletRequest request, HttpServletResponse response) {
        String phone = map.get("phone").toString();
        String code = map.get("verifyCode").toString();

        if (!baseService.existsByPhone(phone)) {
            return Result.error("请先注册");
        }

        try {
            // 构建 Authentication 对象
            SmsCodeAuthenticationToken authenticationToken = new SmsCodeAuthenticationToken(phone,code);
            // 进行认证
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            // 认证成功时调用
            redisUtil.set(RedisConstance.LOGIN_USER_TOKEN + phone, JSON.toJSONString(authentication.getPrincipal()));
            securityAuthenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);
        } catch (BadCredentialsException e) {
            // 认证失败时调用
            smsCodeAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
        } catch (AuthenticationException e) {
            return Result.error(e.getMessage());
        }
        return null;
    }

    @PostMapping("/user/verify")
    @Operation(summary = "找回密码验证")
    public Result<String> verify (@RequestBody Map map, HttpSession session) {
        String phone = map.get("phone").toString();
        String code = map.get("verifyCode").toString();

        if (!baseService.existsByPhone(phone)) {
            return Result.error("请先注册");
        }

        if (!verifyCode(phone, code, session)) {
            return Result.error("验证码错误或已过期");
        }

        return Result.ok("验证成功");
    }

    @PostMapping("/user/reset")
    @Operation(summary = "重置密码")
    public Result<String> reset(@RequestBody ResetPasswordVO user){
        String password = user.getPassword();
        String confirmPassword = user.getConfirmPassword();
        boolean isValidPassword = password.matches("^(?=.*[A-Za-z])(?=.*\\d).{8,}$");

        if (!isValidPassword) {
            return Result.error("密码需由字母和数字组成，且长度至少为8");
        }
        if (!password.equals(confirmPassword)) {
            return Result.error("两次输入的密码不一致");
        }
        user.setPassword(passwordEncoder.encode(password));
        baseService.resetPassword(user);
        return Result.ok("密码设置成功，去登录吧！");
    }

    /**
     * 验证手机号格式的方法
     */
    public static boolean validatePhoneNumber(String phone) {
        String pattern = "^1[3456789]\\d{9}$";
        return Pattern.matches(pattern, phone) && phone.length() == 11;
    }

    /**
     * 验证验证码是否正确
     */
    private boolean verifyCode(String phone, String code, HttpSession session) {
        String savedCode = (String) session.getAttribute(phone + "_code");
        Long savedTimestamp = (Long) session.getAttribute(phone + "_timestamp");
        if (savedCode != null && savedTimestamp != null) {
            if (!savedCode.equals(code)) {
                // 验证码不正确
                return false;
            }
            // 验证码已过期
            return Instant.now().toEpochMilli() - savedTimestamp < 5 * 60 * 1000;
        }
        // 没有找到保存的验证码
        return false;
    }

    @GetMapping("/buyer/user/getUserInfo")
    @Operation(summary = "获取登录用户信息")
    public Result<UserInfoVo> getUserInfo(){
        Long userId = IdHolder.getUserId();
        User user = userMapper.getUserById(userId);
        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setId(userId);
        userInfoVo.setNickname(user.getNickName());
        userInfoVo.setProfile(user.getProfile());
        if(user.getHaveStore()){
            userInfoVo.setStoreId(user.getStoreId());
        }
        // 从后端生成并返回userSig
        String userSig = tlsSigAPIv2.genUserSig(String.valueOf(userId), 86400);
        userInfoVo.setUserSig(userSig);
        return Result.ok(userInfoVo);
    }

    @PostMapping("/buyer/user/logout")
    @Operation(summary = "退出登录")
    public Result<String> logout(){
        LoginUser loginUser = (LoginUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String key = RedisConstance.LOGIN_USER_TOKEN + loginUser.getUser().getPhone();
        redisUtil.delete(key);
        cartInfoService.asyncSyncCartWithDatabaseOnLogout(loginUser.getUser().getId());
        footPrintService.asyncSyncFootWithDatabaseOnLogout(loginUser.getUser().getId());
        return Result.ok("");
    }


}